﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UMLSynthesizer.LangAnalyzer.Elements;

namespace UMLSynthesizer.CSharpAnalyzer.Helpers
{
    class CSharpElementWithAttributesHelper
    {
        #region Fields

        IEnumerable<SyntaxNode> elementNodes;
        VisibilityStates defaultVisibility;
        bool forceAbstract;
        bool forceStatic;

        #endregion

        #region Constructors

        public CSharpElementWithAttributesHelper(SyntaxNode elementNode,
            VisibilityStates defaultVisibility = VisibilityStates.Private,
            bool forceAbstract = false, bool forceStatic = false)
            : this(new SyntaxNode[] { elementNode }, defaultVisibility, forceAbstract, forceStatic) { }
        public CSharpElementWithAttributesHelper(IEnumerable<SyntaxNode> elementNodes,
            VisibilityStates defaultVisibility = VisibilityStates.Private,
            bool forceAbstract = false, bool forceStatic = false)
        {
            this.elementNodes = elementNodes;
            this.defaultVisibility = defaultVisibility;
            this.forceAbstract = forceAbstract;
            this.forceStatic = forceStatic;
        }

        #endregion

        #region Properties

        public bool IsAbstract
        {
            get
            {
                if (forceAbstract)
                    return true;

                var elementNode = elementNodes.First();
                switch (elementNode.CSharpKind())
                {
                    case SyntaxKind.ClassDeclaration:
                        return CheckAbstract((elementNode as ClassDeclarationSyntax).Modifiers);
                    case SyntaxKind.ConstructorDeclaration:
                        return CheckAbstract((elementNode as ConstructorDeclarationSyntax).Modifiers);
                    case SyntaxKind.DelegateDeclaration:
                        return CheckAbstract((elementNode as DelegateDeclarationSyntax).Modifiers);
                    case SyntaxKind.EnumDeclaration:
                        return CheckAbstract((elementNode as EnumDeclarationSyntax).Modifiers);
                    case SyntaxKind.FieldDeclaration:
                        return CheckAbstract((elementNode as FieldDeclarationSyntax).Modifiers);
                    case SyntaxKind.InterfaceDeclaration:
                        return CheckAbstract((elementNode as InterfaceDeclarationSyntax).Modifiers);
                    case SyntaxKind.MethodDeclaration:
                        return CheckAbstract((elementNode as MethodDeclarationSyntax).Modifiers);
                    case SyntaxKind.PropertyDeclaration:
                        return CheckAbstract((elementNode as PropertyDeclarationSyntax).Modifiers);
                    case SyntaxKind.StructDeclaration:
                        return CheckAbstract((elementNode as StructDeclarationSyntax).Modifiers);
                    default:
                        return false;
                }
            }
        }
        public bool IsStatic
        {
            get
            {
                if (forceStatic)
                    return true;

                var elementNode = elementNodes.First();
                switch (elementNode.CSharpKind())
                {
                    case SyntaxKind.ClassDeclaration:
                        return CheckStatic((elementNode as ClassDeclarationSyntax).Modifiers);
                    case SyntaxKind.ConstructorDeclaration:
                        return CheckStatic((elementNode as ConstructorDeclarationSyntax).Modifiers);
                    case SyntaxKind.DelegateDeclaration:
                        return CheckStatic((elementNode as DelegateDeclarationSyntax).Modifiers);
                    case SyntaxKind.EnumDeclaration:
                        return CheckStatic((elementNode as EnumDeclarationSyntax).Modifiers);
                    case SyntaxKind.FieldDeclaration:
                        return CheckStatic((elementNode as FieldDeclarationSyntax).Modifiers);
                    case SyntaxKind.InterfaceDeclaration:
                        return CheckStatic((elementNode as InterfaceDeclarationSyntax).Modifiers);
                    case SyntaxKind.MethodDeclaration:
                        return CheckStatic((elementNode as MethodDeclarationSyntax).Modifiers);
                    case SyntaxKind.PropertyDeclaration:
                        return CheckStatic((elementNode as PropertyDeclarationSyntax).Modifiers);
                    case SyntaxKind.StructDeclaration:
                        return CheckStatic((elementNode as StructDeclarationSyntax).Modifiers);
                    default:
                        return false;
                }
            }
        }
        public VisibilityStates Visibility
        {
            get
            {
                var elementNode = elementNodes.First();
                switch (elementNode.CSharpKind())
                {
                    case SyntaxKind.ClassDeclaration:
                        return CheckVisibility((elementNode as ClassDeclarationSyntax).Modifiers);
                    case SyntaxKind.ConstructorDeclaration:
                        return CheckVisibility((elementNode as ConstructorDeclarationSyntax).Modifiers);
                    case SyntaxKind.DelegateDeclaration:
                        return CheckVisibility((elementNode as DelegateDeclarationSyntax).Modifiers);
                    case SyntaxKind.EnumDeclaration:
                        return CheckVisibility((elementNode as EnumDeclarationSyntax).Modifiers);
                    case SyntaxKind.FieldDeclaration:
                        return CheckVisibility((elementNode as FieldDeclarationSyntax).Modifiers);
                    case SyntaxKind.GetAccessorDeclaration:
                    case SyntaxKind.SetAccessorDeclaration:
                        return CheckVisibility((elementNode as AccessorDeclarationSyntax).Modifiers);
                    case SyntaxKind.InterfaceDeclaration:
                        return CheckVisibility((elementNode as InterfaceDeclarationSyntax).Modifiers);
                    case SyntaxKind.MethodDeclaration:
                        return CheckVisibility((elementNode as MethodDeclarationSyntax).Modifiers);
                    case SyntaxKind.PropertyDeclaration:
                        return CheckVisibility((elementNode as PropertyDeclarationSyntax).Modifiers);
                    case SyntaxKind.StructDeclaration:
                        return CheckVisibility((elementNode as StructDeclarationSyntax).Modifiers);
                    default:
                        return VisibilityStates.Unknown;
                }
            }
        }

        #endregion

        #region Private helpers

        bool CheckAbstract(SyntaxTokenList syntaxTokenList)
        {
            return (from syntaxToken in syntaxTokenList
                    where syntaxToken.Text == "abstract"
                    select syntaxToken).Any();
        }
        bool CheckStatic(SyntaxTokenList syntaxTokenList)
        {
            return (from syntaxToken in syntaxTokenList
                    where syntaxToken.Text == "static"
                    select syntaxToken).Any();
        }
        private VisibilityStates CheckVisibility(SyntaxTokenList syntaxTokenList)
        {
            foreach (var syntaxToken in syntaxTokenList)
            {
                switch (syntaxToken.Text)
                {
                    case "internal":
                        return VisibilityStates.Internal;
                    case "private":
                        return VisibilityStates.Private;
                    case "protected":
                        return VisibilityStates.Protected;
                    case "public":
                        return VisibilityStates.Public;
                }
            }
            return defaultVisibility;
        }

        #endregion
    }
}
